using System;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using AutoMapper;
using Common.Commands;
using Common.Views;
using Core.Infrastructure.CustomExceptions;
using Common.Enumerations;
using Sanelib.Core;
using Sanelib.DataOperation;
using Sanelib.Dto;
using StructureMap;
using WebApp.Common;
using WebApp.Services;

namespace WebApp.ControllerApis
{
    public class SmartApiController : ApiController
    {
        protected IUserSession UserSession { get; private set; }
        protected IMappingEngine MappingEngine { get; private set; }

        public SmartApiController(IUserSession userSession, IMappingEngine mappingEngine)
        {
            UserSession = userSession;
            MappingEngine = mappingEngine;
        }

        protected void EnsureCurrentUserHas(Permission permission)
        {
            var currentUser = UserSession.GetCurrentUser();
            if (!currentUser.HasPermission(permission))
                throw new HttpResponseException(HttpStatusCode.Unauthorized);
        }

        protected bool CurrentUserHas(Permission permission)
        {
            var currentUser = UserSession.GetCurrentUser();
            return currentUser.HasPermission(permission);
        }

        protected MemberView GetCurrentUser()
        {
            return UserSession.GetCurrentUser();
        }

        protected HttpResponseMessage ExecuteCommand<TCommand>(TCommand command) where TCommand : ICommand
        {
            var validationResult = command.Validate();

            var response = default(dynamic);

            if (!validationResult.IsValid)
                return Content(validationResult);
            
            try
            {
                var auditedCommand = command as AuditedCommand;

                MemberView performingUser;

                if (auditedCommand != null)
                {
                    performingUser = UserContext.GetCurrentUser();

                    if (performingUser == null)
                    {
                        throw new DomainProcessException("User session is invalid");
                    }
                    auditedCommand.UserId = performingUser.Id; 
                }
                else
                {
                    performingUser = UserContext.GetAnonymousUser();
                }

                var commandProcessor = ObjectFactory.GetInstance<ICommandProcessor<TCommand>>();

                var instance = ObjectFactory.GetInstance<IUnitOfWork>();

                instance.Begin();

                ObjectFactory.GetInstance<ICommandRecorder>().Record(command, performingUser.Id, performingUser.Name);

                try
                {
                    response = commandProcessor.Process(command);
                    instance.Commit();
                }
                catch
                {
                    instance.RollBack();
                    throw;
                }
                finally
                {
                    instance.Dispose();
                }
            }
            catch (DomainProcessException domainException)
            {
                validationResult.AddError("Form", domainException.GetBaseException().Message);
            }
            catch (Exception e)
            {
                Logger.Log(LogType.Error, typeof(SmartApiController), "Internal", e);
                validationResult.AddError("Internal", e.GetBaseException().Message);
            }

            return validationResult.IsValid ? Content(response) : Content(validationResult);
        }

        protected HttpResponseMessage AutoMappedContent<TModel, TViewModel>(TModel model)
        {
            var response = MappingEngine.Map<TModel, TViewModel>(model);
            return Content(response);
        }

        protected HttpResponseMessage Content<T>(T response)
        {
            var httpStatusCode = HttpStatusCode.OK;

            if (typeof(T) == typeof(ErrorResult))
            {
                httpStatusCode = HttpStatusCode.NotAcceptable;
            }

            return Request.CreateResponse(httpStatusCode, response);
        }
    }
}